package com.log4jviewer.ui.views;

import org.eclipse.jface.action.IAction;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.log4jviewer.Activator;
import com.log4jviewer.ErrorListener;
import com.log4jviewer.ErrorsContainer;
import com.log4jviewer.domain.LogField;
import com.log4jviewer.domain.LogList;
import com.log4jviewer.filters.FilterModel;
import com.log4jviewer.ui.preferences.PreferencePage;
import com.log4jviewer.ui.preferences.PreferencePageListener;
import com.log4jviewer.ui.preferences.additional.AdditionalPreferencePage;
import com.log4jviewer.ui.preferences.additional.AdditionalPreferencePageListener;
import com.log4jviewer.ui.preferences.additional.ColumnModel;
import com.log4jviewer.ui.preferences.additional.ColumnModelList;
import com.log4jviewer.ui.preferences.additional.ColumnModelListListener;
import com.log4jviewer.ui.preferences.filters.FilterPreferencePage;
import com.log4jviewer.ui.preferences.filters.FilterPreferencePageListener;

/**
 * Class represents a View where logs are displayed.
 * 
 * @author <a href="mailto:rd.ryly@gmail.com">Ruslan Diachenko</a>
 */
public class LogView extends ViewPart implements AdditionalPreferencePageListener, FilterPreferencePageListener,
        PreferencePageListener, ColumnModelListListener, ErrorListener {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private static Table table;

    private TableViewer tableViewer;

    private IAction startServerAction;

    private IAction stopServerAction;

    private IAction errorViewAction;

    private boolean errorActionEnabled;

    private LogViewController logViewController;

    private ViewLifecycleListener viewLifecycleListener;

    public Table getTable() {
        return table;
    }

    public static int getColumnWidth(final int columnIndex) {
        return table.getColumn(columnIndex).getWidth();
    }

    public LogList getLogList() {
        return logViewController.getLogList();
    }

    public ErrorsContainer getErrorManager() {
        return logViewController.getErrorManager();
    }

    public void setStartActionState(final boolean enabled) {
        if (startServerAction != null) {
            startServerAction.setEnabled(enabled);
            logger.debug("Start server button state: {}", enabled);
        }
    }

    public void setStopActionState(final boolean enabled) {
        if (stopServerAction != null) {
            stopServerAction.setEnabled(enabled);
            logger.debug("Stop server button state: {}", enabled);
        }
    }

    public void setErrorActionState(final boolean enabled) {
        if (errorViewAction != null) {
            errorViewAction.setEnabled(enabled);
            errorActionEnabled = enabled;
            logger.debug("Error view button state: {}", enabled);
        }
    }

    public boolean isErrorActionEnabled() {
        return errorActionEnabled;
    }

    private void initializeServerActions() {
        // Set server actions
        startServerAction = logViewController.getStartServerAction(getViewSite());
        logger.debug("'Start Server' button was initialized.");
        stopServerAction = logViewController.getStopServerAction(getViewSite());
        logger.debug("'Stop Server' button was initialized.");
        errorViewAction = logViewController.getErrorViewAction(getViewSite());
        logger.debug("'Error View' button was initialized.");

        setErrorActionState(false);

        // If Server 'Automatic start' options is true, then start server and change server actions state.
        if (logViewController.isServerStarted()) {
            startServer();
            setStartActionState(false);
            setStopActionState(true);
        } else {
            setStartActionState(true);
            setStopActionState(false);
        }
    }

    @Override
    public void createPartControl(final Composite parent) {
        // Create controller for this view
        logViewController = new LogViewController();
        logViewController.init();
        getErrorManager().addListener(this);
        logViewController.initializeServerSettings();

        // Create the table 
        createTable(parent);

        // Create and setup the TableViewer
        createTableViewer();

        // Add lifecycle listener to this view
        viewLifecycleListener = new ViewLifecycleListener();
        getViewSite().getPage().addPartListener(viewLifecycleListener);
        logger.debug("Lifecycle listener was added to LogView");

        AdditionalPreferencePage.addListener(this);
        FilterPreferencePage.addListener(this);
        PreferencePage.addListener(this);
        ColumnModelList.addListener(this);
    }

    @Override
    public void setFocus() {
        getViewSite().getPage().activate(this);
        tableViewer.getControl().setFocus();
        logger.debug("LogView in Focus.");
    }

    private void createTable(final Composite parent) {
        int style = SWT.SINGLE | SWT.BORDER | SWT.FULL_SELECTION | SWT.HIDE_SELECTION;
        table = new Table(parent, style);

        GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        table.setLayoutData(gridData);

        table.setLinesVisible(true);
        table.setHeaderVisible(true);

        addColumnsToTable();
    }

    private void addColumnsToTable() {
        final IPreferenceStore store = Activator.getInstance().getPreferenceStore();
        ColumnModel[] columnItems = new ColumnModelList(store).getColumnItems();

        for (final ColumnModel columnModel : columnItems) {
            final TableColumn column = new TableColumn(table, SWT.NONE);
            int columnIndex = columnModel.getColumnIndex();
            column.setText(LogField.values()[columnIndex].getName());
        }
        logViewController.updateColumnsDisplay(table, columnItems);
    }

    private void createTableViewer() {
        tableViewer = new TableViewer(table);
        tableViewer.setUseHashlookup(true);

        tableViewer.setContentProvider(new LogViewContentProvider(getLogList()));

        // The input for the table viewer is the instance of LogList
        tableViewer.setInput(getLogList().getFilteredLogs());

        // Selection provider for the view.
        getViewSite().setSelectionProvider(tableViewer);
    }

    @Override
    public void dispose() {
        super.dispose();

        // Remove lifecycle listener from this view
        getViewSite().getPage().removePartListener(viewLifecycleListener);
        logger.debug("Lifecycle listener was removed from LogView");

        AdditionalPreferencePage.removeListener(this);
        FilterPreferencePage.removeListener(this);
        PreferencePage.removeListener(this);
        ColumnModelList.removeListener(this);
        getErrorManager().removeListener(this);

        logViewController.dispose();
    }

    public void clearLogTable() {
        table.removeAll();
        logger.debug("LogView table was cleared.");
    }

    public void addLogTableListener(final Listener listener) {
        table.addListener(SWT.Selection, listener);
        logger.debug("Selection listener for LogView table was added.");
    }

    public void removeLogTableListener(final Listener listener) {
        table.removeListener(SWT.Selection, listener);
        logger.debug("Selection listener for LogView table was removed.");
    }

    @Override
    public void handleAdditionalPreferenceEvent(final AdditionalPreferencePage event, final ColumnModel[] columnItems) {
        logViewController.updateColumnsDisplay(table, columnItems);
    }

    @Override
    public void handleFilterPreferenceEvent(final FilterPreferencePage event, final FilterModel filterModel) {
        logViewController.setLogFilter(filterModel);
        clearLogTable();
        getLogList().revalidate();
    }

    @Override
    public void handleMainPreferenceEvent(final PreferencePage event) {
        logViewController.updateLogBufferSize();
        logViewController.initializeServerSettings();
    }

    @Override
    public void handleColumnModelListEvent(final ColumnModelList event, final int[] swappingColumnIndexes) {
        logViewController.updateColumns(table, swappingColumnIndexes);
    }

    @Override
    public void handleErrorManagerEvent(final ErrorsContainer event) {
        setErrorActionState(true);
    }

    // Controller wrapper methods
    public void startServer() {
        logViewController.startServer();
    }

    public boolean isServerAlive() {
        return logViewController.isServerAlive();
    }

    public void stopServer() {
        logViewController.stopServer();
    }

    public IViewPart openView(final String viewId) {
        IViewPart view = null;

        try {
            IWorkbenchPage workbenchPage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
            view = workbenchPage.findView(viewId);

            if (view == null) {
                view = workbenchPage.showView(viewId);
            }
        } catch (PartInitException e) {
            String errorMessage = viewId + " view couldn't be initialized!";
            getErrorManager().addError(errorMessage);
            logger.error(errorMessage, e);
        }
        return view;
    }

    // Inner class represents handler for LogView life cycle actions: close/open view, activate/deactivate view, etc.
    private class ViewLifecycleListener implements IPartListener {

        private Listener logTableListener;

        @Override
        public void partActivated(final IWorkbenchPart part) {
            // no code
        }

        @Override
        public void partBroughtToTop(final IWorkbenchPart part) {
            // no code
        }

        @Override
        public void partClosed(final IWorkbenchPart part) {
            if (Activator.getInstance().getPluginId().equals(part.getSite().getPluginId())) {

                if (part instanceof LogView) {
                    removeLogTableListener(logTableListener);
                    logger.debug("LogView part was closed.");
                }
            }
        }

        @Override
        public void partDeactivated(final IWorkbenchPart part) {
            // no code
        }

        @Override
        public void partOpened(final IWorkbenchPart part) {
            if (Activator.getInstance().getPluginId().equals(part.getSite().getPluginId())) {

                if (part instanceof LogView) {
                    initializeServerActions();
                    logger.debug("LogView part was opened.");

                    // listener for log table in LogView
                    logTableListener = new Listener() {
                        @Override
                        public void handleEvent(final Event event) {
                            openView(DetailLogView.ID);
                            setFocus();
                        }
                    };
                    addLogTableListener(logTableListener);
                }
            }
        }
    }
}